package com.css.common.core.config;

import java.io.IOException;
import java.lang.reflect.Type;
import java.nio.charset.Charset;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;

import com.css.common.core.exception.ServiceException;
import com.css.common.core.utils.GyUtils;
import com.css.common.core.utils.yl.SM2;
import com.css.common.core.utils.yl.YlswUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;

import com.alibaba.fastjson.JSONObject;

import cn.hutool.core.io.IoUtil;
import cn.hutool.core.util.CharsetUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.digest.DigestUtil;
import feign.FeignException;
import feign.RequestInterceptor;
import feign.RequestTemplate;
import feign.Response;
import feign.codec.DecodeException;
import feign.codec.Decoder;
import lombok.extern.slf4j.Slf4j;

/**
 * @description: 银联商务银行卡相关的feign配置
 * @Title: YlswYhkFeignConfig
 * @Author zhuowugong
 * @Package com.invoice.config
 * @Date 2024/8/17 15:37
 */
@Slf4j
public class YlswYhkFeignConfig {

    @Value("${ylsw.appId:}")
    private String appId;

    @Value("${ylsw.appKey:}")
    private String appKey;
    
    //请求报文平台公钥
    @Value("${ylsw.ptPubKey:}")
    private String ptPubKey;
    
    //应答报文商户私钥
    @Value("${ylsw.shPriKey:}")
    private String shPriKey;

    private final String queryYhkfksd = "queryYhkfksd";//银行卡发卡属地查询接口，请求头认证，请求报文加密，返回报文体需解密形式
    private final String getYlswAccessToken = "getYlswAccessToken";//获取token接口，请求头认证，请求报文加密，返回报文无需解密
    private final DateTimeFormatter dateTimeFormatter =  DateTimeFormatter.ofPattern("yyyyMMddHHmmss");
    private final String signMethod = "SHA256";
    
    @Bean
    public RequestInterceptor interceptor() {
        return new RequestInterceptor() {
            @Override
            public void apply(RequestTemplate template) {
            	final String method = template.methodMetadata().method().getName();
            	if (queryYhkfksd.equals(method)) {
            		//银行卡发卡属地查询接口需要加密
            		final byte[] body = template.body();
                	if (GyUtils.isNull(body)) {
                        throw new ServiceException("银联商务中台接口请求报文体为空");
                    }
                	if (GyUtils.isNull(appId)) {
                        throw new ServiceException("银联商务中台接口请求中appId不能为空");
                    }
                	if (GyUtils.isNull(appKey)) {
                        throw new ServiceException("银联商务中台接口请求中appKey不能为空");
                    } 
                	if (GyUtils.isNull(ptPubKey)) {
                        throw new ServiceException("银联商务中台接口请求中平台SM2公钥不能为空");
                    }
                	if (GyUtils.isNull(shPriKey)) {
                        throw new ServiceException("银联商务中台接口请求中商户SM2秘钥不能为空");
                    }
                	final JSONObject req = new JSONObject();           	
                    //SM2公钥进行加密，然后对加密后的报文用base64编码
                    final String str = StrUtil.str(body, CharsetUtil.UTF_8);
                    final String sm2Body = SM2.sm2Encrypt(JSONObject.parse(str).toString(), ptPubKey);
                    req.put("data", sm2Body);
                    //认证
                    String authorization=null;
    				try {
    					authorization = YlswUtils.getOpenBodySig(appId, appKey, req.toJSONString().getBytes());
    				} catch (Exception e) {
    					 log.error("银联商务生成Authorization认证信息失败:{}", e.getMessage());
    		             throw new RuntimeException(e);
    				}
                    template.header("Content-Type", "application/json;charset=utf-8")
                	.header("Authorization", authorization);
                    template.body(req.toJSONString());
            	} else if (getYlswAccessToken.equals(method)) {
            		if (GyUtils.isNull(appId)) {
                        throw new ServiceException("银联商务中台接口请求appId为空");
                    }
                	if (GyUtils.isNull(appKey)) {
                        throw new ServiceException("银联商务中台接口请求appKey为空");
                    }
                	 //设置请求头
                    template.header("Content-Type", "application/json;charset=utf-8");  
                    final JSONObject req = new JSONObject();
                    final String timestamp = LocalDateTime.now().format(dateTimeFormatter);
                    final String nonce = IdUtil.fastSimpleUUID();
                    //signature=SHA256_hex(appId+timestamp+nonce+appKey)sha256加密,返回64位十六进制字符串
                    final String signature = DigestUtil.sha256Hex(appId+timestamp+nonce+appKey);
                    req.put("appId", appId);
                    req.put("timestamp", timestamp);
                    req.put("nonce", nonce);
                    req.put("signMethod", signMethod);
                    req.put("signature", signature);
                    template.body(req.toString());
            	}           	
            }
        };
    }

    @Bean
    public Decoder decoder() {
        return new Decoder() {
            @Override
            public Object decode(Response response, Type type) throws IOException, DecodeException, FeignException {
                final String content = IoUtil.read(response.body().asInputStream(),Charset.forName(CharsetUtil.UTF_8));
                log.info("返回报文体body:{}",content);
                final JSONObject jsonObject = JSONObject.parseObject(content);
                final String data = jsonObject.getString("data");
                final String method = response.request().requestTemplate().methodMetadata().method().getName();
                if (GyUtils.isNotNull(data) && queryYhkfksd.equals(method)) {
                	//银行卡发卡属地查询接口需要解密
                	jsonObject.remove("data");               	
                	final String decData = SM2.sm2Decrypt(data, shPriKey);
                	log.info("decData解密后数据:{}",decData);
                	jsonObject.put("data",JSONObject.parseObject(decData));
                }
                Object result = jsonObject.toJavaObject(type);
                return result;
            }
        };
    }

}
